Technical Q&A QA1262
Calculating the video frame rate of an MPEG-1 or MPEG-2 movie


Q: MPEG-1 または MPEG-2 のムービーのビデオフレームレートを計算するにはどうすればよいでしょうか?

A: この情報を取得する最も簡単な方法は、この関数は QuickTime 5 で導入された、新しい MediaGetPublicInfo 関数を使うことです(「MediaHandlers.h」を参照)。この関数にセレクタ kMHInfoEncodedFrameRate を渡すだけです。

また、MPEG メディアについては、QuickTime 6 の不具合(r. 3236091)があり、MediaGetPublicInfo を呼び出してフレームレート情報を取得する前に、MoviesTask 関数を使ってムービーをタスク化する必要があります。

(新しい MPEG-4 形式を含む)ほかのメディア形式の場合は、GetMediaSampleCount(または GetMovieNextInterestingTime)を使ってフレーム数をカウントし、この値とともにメディアの再生時間とタイムスケールを使ってフレームレートを割り出すという「古い」方法でフレームレートを計算する必要があります。下のリスト 1 にこの方法を示します。



リスト 1 MPEG ムービーのビデオフレームレートの計算

#include <QuickTime/QuickTime.h>

#define     kCharacteristicHasVideoFrameRate    FOUR_CHAR_CODE('vfrr')
#define     kCharacteristicIsAnMpegTrack        FOUR_CHAR_CODE('mpeg')
#define BailOnMoviesErr(err) {if (err) goto bail; }

OSErr GetMovieFPS(Movie theMovie, short *fps)
{
    MediaHandler mpegMediaHandler = nil;
    Track        theTrack = nil;
    Media        myMedia = nil;
    OSErr        err = noErr;
    Boolean      isMpeg = false;

        /* フレームレート値を初期化 */
    *fps = 0;
    
    if (theMovie == nil) goto bail;
    
        /* ビデオトラックを取得 */
    theTrack = GetMovieIndTrackType(theMovie,
                                    1,
                                    kCharacteristicHasVideoFrameRate,
                                    movieTrackCharacteristic);
    err = GetMoviesError();
    BailOnMoviesErr(err != noErr);
    
        /* トラックのメディアを取得 */
    myMedia = GetTrackMedia(theTrack);
    err = GetMoviesError();
    BailOnMoviesErr(err != noErr);
    
        /* メディアハンドラのコンポーネントへの参照を取得 */
    mpegMediaHandler = GetMediaHandler(myMedia);
    err = GetMoviesError();
    BailOnMoviesErr(err != noErr);
    
        /* これは mpeg メディアハンドラか? */
    err = MediaHasCharacteristic(mpegMediaHandler,
                                 kCharacteristicIsAnMpegTrack,
                                 &isMpeg);
    if ((err == noErr) && isMpeg)
    {
        MHInfoEncodedFrameRateRecord encodedFrameRate;
        Size encodedFrameRateSize = sizeof(encodedFrameRate);
            
            /* QuickTime の不具合により、フレームレート値を
               取得する前にムービーをタスク化する必要がある */
        MoviesTask( theMovie, 0 );
        
            /* 静的なフレームレートを取得 */
        err = MediaGetPublicInfo(mpegMediaHandler,
                                 kMHInfoEncodedFrameRate,
                                 &encodedFrameRate,
                                 &encodedFrameRateSize);
if (err == noErr)
        {                
            Fixed staticFrameRate = 0;

            staticFrameRate = encodedFrameRate.encodedFrameRate;
                /* この値を簡単にするために丸める。端数も取得するには、
                   この値を分析する */
            *fps = FixRound(staticFrameRate);
        }
    }
    else /* ここでは、MPEG 以外のメディアを扱うため、
            「古い」方法を使う */
    {
        long sampleCount = 0;
        
            /* メディアのサンプル数を取得 */
        sampleCount = GetMediaSampleCount(myMedia);
        err = GetMoviesError();
        BailOnMoviesErr(err != noErr);
        
        if (sampleCount)
        {
            TimeValue duration;
            TimeValue timeScale;
            Fixed staticFrameRate = 0;
            double frameRate;
            
                /* メディアの再生時間を取得 */
            duration = GetMediaDuration(myMedia);
            err = GetMoviesError();
            BailOnMoviesErr(err != noErr);
            
                /* メディアのタイムスケールを取得 */
            timeScale = GetMediaTimeScale(myMedia);
            err = GetMoviesError();
            BailOnMoviesErr(err != noErr);
            
            /* フレームレートを計算:
            フレームレート =
          (サンプル数 * メディアのタイムスケール)/ メディアの再生時間 */
            frameRate = sampleCount*(double)timeScale/(double)duration;
            staticFrameRate = X2Fix(frameRate);
                /* この値を簡単にするために丸める - その上、この
                   値を分析して端数の 1 部分を得ることもできる */
            *fps = FixRound(staticFrameRate);
        }
    }

bail:

return err;
}




もう 1 つの方法として、QTMTB 54 に記述されているように、GetMovieNextInterestingTime 関数を使って MPEG ビデオフレームをカウントし、次にメディアの再生時間とタイムスケールの値を使ってフレームレートを計算する方法もあります。下のリスト 2 のコードは、この方法を示しています。



リスト 2 - GetMovieNextInterestingTime を使用したフレームレートの計算

#include <QuickTime/QuickTime.h>

long GetFrameCount (Movie theMovie)
{   
    long        frameCount = 0;
    TimeValue   curMovieTime;
    
    if (theMovie == NULL)
        goto bail;
    
        // QuickTime 6 の不具合により、初めに
        // ムービーをタスク化する必要がある
    MoviesTask( theMovie, 0 );
    
    curMovieTime = 0;
    while( curMovieTime >= 0 ) 
    {
        GetMovieNextInterestingTime( 
                                    theMovie, 
                                    nextTimeStep,
                                    0, NULL,
                                    curMovieTime,
                                    fixed1,
                                    &curMovieTime,
                                    NULL );
        frameCount++;
    }
    
    frameCount--; // ムービーの最後に余分な時間をとるステップがある

bail:
    
    return(frameCount);
}

OSErr GetMovieFPS(Movie theMovie, TimeValue *fps)
{
    OSErr err = noErr;
    TimeValue duration=0, 
              timeScale=0,
              movieDurationInSeconds;
                    
        /* fps 値の初期化 */
    *fps = 0;
    
        /* ムービーの再生時間を取得 */
    duration = GetMovieDuration(theMovie);
    err = GetMoviesError();
    BailOnMoviesErr(err != noErr);
    
        /* ムービーのタイムスケールを取得 */
    timeScale = GetMovieTimeScale(theMovie);
    err = GetMoviesError();
    BailOnMoviesErr(err != noErr);

        /* ムービーの長さを秒単位で取得 */
    movieDurationInSeconds = duration / timeScale;
        /* フレームレートを計算 */
    *fps = GetFrameCount (theMovie) / movieDurationInSeconds;

bail:

    return(err) ;
}





[2003 年 4 月 21 日]